<?php
/**
 * Date: 2024.8
 * Author: kahn
 * redis驱动
 */
namespace driver;

use RedisException;

class Redis
{
    protected string $key; //数据库配置名
    private static array $connections = array();
    private \Redis $redis;
    
    public function __construct($key){
        if(!isset(self::$connections[$key])){
            $config = \Yaf_Registry::get("config")->cache->$key->toArray();
            if($config == null){
                \Log::error('can not find the redis in config: ' . $key);
                exit;
            }
            self::$connections[$key] = self::connect($config);
        }
        $this->redis = self::$connections[$key];
    }
    
    public function getRedis(){
        return $this->redis;
    }

    /**
     * @throws RedisException
     */
    private static function connect($config): \Redis
    {
        try{
            $redis = new \Redis();
            $redis->connect($config['host'], $config['port'], $config['timeout']);
            $redis->auth($config['auth']);
            $redis->select($config['db']);

            return $redis;
        }catch(RedisException $e){
            \Log::error('connect redis error');
            throw $e;
        }
    }

	//===============对key操作=================================

    public function exists($key){
    	return $this->redis->exists($key);
    }
    
    public function delete($key){
    	return $this->redis->del($key);
    }

    public function expire($key, $ttl){
        return $this->redis->expire($key, $ttl);
    }

    //================string 操作函数开始========================
    public function set($key, $value, $expire=null){
        if($expire){
            return $this->setex($key, $expire, $value);
        }else{
            return $this->redis->set($key, $value);
        }
    }
    
    public function get($key){
    	return $this->redis->get($key);
    }
    
    public function incrby($key, $val) {
    	return $this->redis->incrby($key, $val);
    }
    
    public function setex($key, $expire, $val) {
    	return $this->redis->setex($key, $expire, $val);
    }
    
    
    //================string 操作函数结束========================
    
    //================hash 操作函数开始==========================
    public function hExists($key, $field){
    	return $this->redis->hExists($key, $field);
    }
    
    public function hGetAll($key){
    	return $this->redis->hGetAll($key);
    }
    
    public function hGet($key, $field){
    	return $this->redis->hGet($key, $field);
    }
    
    public function hSet($key, $field, $value){
    	return $this->redis->hSet($key, $field, $value);
    }
    
    public function hIncrBy($key, $field, $integer){
    	return $this->redis->hIncrBy($key, $field, $integer);
    }
    public function hDel($key, $field){
    	return $this->redis->hDel($key, $field);
    }
    
    public function hMget($key, $fields){
    	return $this->redis->hMget($key, $fields);
    }
    
    //================hash 操作函数结束==========================
    
    
    //==================set 操作函数开始=========================
    public function sAdd($key, $value){
    	return $this->redis->sAdd($key, $value);
    }
    
    public function sIsMember($key, $value){
    	return $this->redis->sismember($key, $value);
    }
    
    //==================set 操作函数结束=========================
    
    
    //==================list 操作函数==============================
    public function lRange($key, $begin, $end){
    	return $this->redis->lRange($key, $begin, $end);
    }
    
    public function lLen($key){
    	return $this->redis->lLen($key);
    }
    
    public function rPush($key,$value){
    	return $this->redis->rPush($key,$value);
    }
    
    public function lTrim($key, $begin, $end){
    	return $this->redis->lTrim($key, $begin, $end);
    
    }
    //==================zset 操作函数开始=========================
    public function zAdd($key,$score,$user_id){
    	return $this->redis->zAdd($key,$score,$user_id);
    }
    
    
    public function zCard($key){
    	return $this->redis->zCard($key);
    }
    
    public function zRank($key,$user_id){
    	return $this->redis->zRank($key,$user_id);
    }
    
    public function zRevRank($key,$user_id){
    	return $this->redis->zRevRank($key,$user_id);
    }
    
    public function zScore($key,$user_id){
    	return $this->redis->zScore($key,$user_id);
    }
    
    public function zRevRange($key, $begin, $end, $withscore=true){
    	return $this->redis->zrevrange($key, $begin, $end, $withscore);
    }
    
    //==================zset 操作函数结束=========================

    //带伴生锁的缓存机制-写入
    public function redis_set($key, $data, $expire){
    	//$redis = RedisService::singleton();
    	$lock_key = $key . '_lock';
    	$need_update = false;
    	if($this->exists($lock_key)){
    		$lock_data = $this->get($lock_key);
    		if($lock_data['status'] == 'lock'){
    			$need_update = true;
    		}
    	}else{
    		$need_update = true;
    	}
    	$lock_data = array('status'=>'read', 'update_time'=>time(), 'expire_seconds'=>$expire);
    	$this->set($lock_key, json_encode($lock_data));
    	$this->set($key, $data);
    }
    
    //带伴生锁的缓存机制-读取
    public function redis_get($key){
    	$now = time();
    	$lock_key = $key . '_lock';
    	//$redis = RedisService::singleton();
    	$exist = false;
    
    	if($this->exists($key)){
    		#echo 1;
    		if($this->exists($lock_key)){
    			#echo 2;
    			$lock_data = json_decode($this->get($lock_key), true);
    			if($now - $lock_data['update_time'] >= $lock_data['expire_seconds']){
    				#echo 3;
    				if($lock_data['status'] == 'read'){ //数据已经过期, 重新取数据
    					#echo 4;
    					$lock_data['status'] = 'lock'; //加锁
    					$lock_data['lock_time'] = $now; //设置加锁时间
    					$this->set($lock_key, json_encode($lock_data));
    					$exist = false;
    				}elseif($lock_data['status'] == 'lock'){ //数据正在由其他链接获取
    					#echo 5;
    					if($now - $lock_data['lock_time'] > 60){ //1分钟还没去到数据, 交由该次请求获取数据
    						#echo 6;
    						$lock_data['lock_time'] = $now; //设置加锁时间
    						$this->set($lock_key, json_encode($lock_data));
    						$exist = false;
    					}else{
    						#echo 7;
    						$exist = true; //锁状态还没有超时, 暂时使用老的数据
    					}
    				}
    			}else{
    				#echo 8;
    				$exist = true;  //没有过期, 继续使用
    			}
    		}else{
    			#echo 9;
    			$exist = false; //如果伴生锁不存在, 那就去竞争加锁吧
    		}
    	}else{
    		#echo l5000003;
    		$exist =false; //如果redis缓存不存在, 那就去竞争加锁吧
    	}
    
    	return $exist ? $this->get($key) : false;
    }


    protected function _formatKey($keyPattern, $param)
    {
        foreach ($param as $k => $v) {
            $keyPattern = str_replace("[$k]", $v, $keyPattern);
        }
        return $keyPattern;
    }

}
